Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 << zurück
Visual C# 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2005

Visual C# 2005
1.320 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-586-X
gp Kapitel 7 Weitere Möglichkeiten von C#
  gp 7.1 Operatorüberladung
    gp 7.1.1 Die Syntax der Operatorüberladung
    gp 7.1.2 Beispiel einer Operatorüberladung
    gp 7.1.3 Überladungsbeispiele
    gp 7.1.4 Benutzerdefinierte Konvertierungen – implizit und explizit
  gp 7.2 Indexer
    gp 7.2.1 Überladen von Indexern
    gp 7.2.2 Parameterbehaftete Eigenschaften
  gp 7.3 Collections (Auflistungen)
    gp 7.3.1 Die elementaren Schnittstellen der Auflistungsklassen
    gp 7.3.2 Die Klasse »ArrayList«
    gp 7.3.3 Das Sortieren der Elemente einer »ArrayList«
    gp 7.3.4 Die Schnittstelle »IDictionary«
    gp 7.3.5 Die Klasse »Hashtable«
    gp 7.3.6 Die Klassen »Queue« und »Stack«
    gp 7.3.7 Objektauflistungen im Überblick
    gp 7.3.8 Benutzerdefinierte Auflistungen
  gp 7.4 Generics – Generische Datentypen
    gp 7.4.1 Die Typproblematik am Beispiel der Klasse »Stack«
    gp 7.4.2 Die Lösung mit einer generischen Klasse
    gp 7.4.3 Typparameter mit Constraints einschränken
    gp 7.4.4 Generische Methoden
    gp 7.4.5 Generics und Vererbung
    gp 7.4.6 Konvertierung von Generics
    gp 7.4.7 Generische Delegate
    gp 7.4.8 Generische Klassen in der .NET-Klassenbibliothek
    gp 7.4.9 Eigene Auflistungen mit »yield« durchlaufen
    gp 7.4.10 Daten durch »null« beschreiben
  gp 7.5 Fortgeschrittene Delegat-Techniken
    gp 7.5.1 Multicast-Delegate
  gp 7.6 Attribute
    gp 7.6.1 Das »Flags«-Attribut
    gp 7.6.2 Anmerkungen zu den Attributen
    gp 7.6.3 Benutzerdefinierte Attribute
  gp 7.7 Unsicherer Programmcode – Zeigertechnik in C#
    gp 7.7.1 Das Schlüsselwort »unsafe«
    gp 7.7.2 Die Deklaration von Zeigern
    gp 7.7.3 Die »fixed«-Anweisung
    gp 7.7.4 Zeigerarithmetik
    gp 7.7.5 Der Operator »->«


Galileo Computing

7.7 Unsicherer Programmcode – Zeigertechnik in C#  downtop

Manchmal ist es erforderlich, auf die Funktionen einer in C geschriebenen herkömmlichen DLL zuzugreifen. Viele C-Funktionen erwarten jedoch Zeiger auf bestimmte Speicheradressen oder geben solche als Aufrufergebnis zurück. Es kann auch vorkommen, dass in einer Anwendung der Zugriff auf Daten erforderlich ist, die sich nicht im Hauptspeicher, sondern beispielsweise im Grafikspeicher befinden. Das Problem ist im ersten Moment, dass C#-Code, der unter der Obhut der Common Language Runtime läuft und als sicherer bzw. verwalteter (managed) Code eingestuft wird, keine Zeiger auf Speicheradressen gestattet.

Ein Entwickler, der mit dieser Einschränkung in seiner Anwendung nicht leben kann, muss unsicheren Code schreiben. Trotz dieser seltsamen Bezeichnung ist unsicherer Code selbstverständlich nicht wirklich »unsicher« oder wenig vertrauenswürdig. Es handelt sich hierbei lediglich um C#-Code, der die Typüberprüfung durch den Compiler einschränkt und den Einsatz von Zeigern und Zeigeroperationen ermöglicht.

Es gibt noch einen zweiten Grund für die Verwendung von unsicherem Code. Die Speicherverwaltung mit dem Garbage Collector hat einen verhältnismäßig großen Verwaltungs-Overhead zur Folge. Codepassagen, die eine bestmögliche Performance erfordern, können als unsicher markiert werden, um die letzten Leistungsreserven auszuschöpfen.


Galileo Computing

7.7.1 Das Schlüsselwort »unsafe«  downtop

Der Kontext, in dem unsicherer Code gewünscht wird, muss mit Hilfe des Schlüsselworts unsafe deklariert werden. Es kann eine komplette Klasse oder eine Struktur ebenso als unsicher markiert werden wie eine einzelne Methode. Es ist sogar möglich, innerhalb des Anweisungsblocks einer Methode einen Teilbereich unsicher zu kennzeichnen.

Ganz allgemein besteht ein nicht sicherer Bereich aus Code, der in geschweiften Klammern eingeschlossen ist und dem das Schlüsselwort unsafe vorangestellt wird. Im folgenden Codefragment wird die Methode Main als unsicher deklariert:


static unsafe void Main(string[] args) {
  // Anweisungen
}

Die Angabe von unsafe ist aber alleine noch nicht ausreichend, um unsicheren Code kompilieren zu können. Zusätzlich muss auch noch der Compilerschalter /unsafe gesetzt werden. Im Visual Studio 2005 legen Sie diesen Schalter im Projekteigenschaftsfenster unter Erstellen · Unsicheren Code zulassen fest. Vergessen Sie, den Compilerschalter einzustellen, wird bei der Kompilierung ein Fehler generiert.


Galileo Computing

7.7.2 Die Deklaration von Zeigerdowntop

In C/C++ sind Zeiger ein klassisches Hilfsmittel der Programmierung, in NET hingegen nehmen Zeiger eine untergeordnete Rolle ein und werden meist nur in Ausnahmesituationen benutzt. Wir werden daher nicht allzu tief in die Thematik einsteigen und uns auf das Wesentlichste konzentrieren. Wenn Sie keine Erfahrungen mit der Zeigertechnik in C oder in anderen zeigerbehafteten Sprachen gesammelt haben und sich dennoch weiter informieren wollen, sollten Sie C-Literatur zur Hand nehmen.

Zeiger sind Verweise auf Speicherbereiche und werden allgemein wie folgt deklariert:


Datentyp * Variable

Dazu ein Beispiel. Mit der Deklaration


int intVar = 4711;
int* pointer;

erzeugen wir eine int-Variable namens intVar und eine Zeigervariable pointer. pointer ist noch kein Wert zugewiesen und zeigt auf eine Speicheradresse, deren Inhalt als Integer interpretiert wird. Der »*«-Operator ermöglicht die Deklaration eines typisierten Zeigers und bezieht sich auf den vorangestellten Typ – hier Integer.

Wollen wir dem Zeiger pointer mitteilen, dass er auf die Adresse der Variablen intVar zeigen soll, müssen wir pointer die Adresse von intVar übergeben:


pointer = &intVar;

Der »&«-Adressoperator liefert eine physikalische Speicheradresse. In der Anweisung wird die Adresse der Variablen intVar ermittelt und dem Zeiger pointer zugewiesen.

Wollen wir den Inhalt der Speicheradresse erfahren, auf die der Zeiger verweist, muss dieser dereferenziert werden:


Console.WriteLine(*pointer);

Das Ergebnis wird 4711 lauten.

Fassen wir den gesamten (unsicheren) Code zusammen. Wenn Sie die Zeigertechnik unter C kennen, werden Sie feststellen, dass es syntaktisch keinen Unterschied gibt:


class Program {
  static unsafe void Main(string[] args) {
    int intVar = 4711;
    int* pointer;
    pointer = &intVar;
    Console.WriteLine(*pointer);
  }
}

C# gibt einen Zeiger nur von einem Wertetyp und niemals von einem Referenztyp zurück. Das gilt jedoch nicht für Arrays und Zeichenfolgen, da Variablen dieses Typs einen Zeiger auf das erste Element bzw. den ersten Buchstaben liefern.


Galileo Computing

7.7.3 Die »fixed«-Anweisundowntop

Während der Ausführung eines Programms werden dem Heap viele Objekte hinzugefügt oder aufgegeben. Um eine unnötige Speicherbelegung oder Speicherfragmentierung zu vermeiden, schiebt der Garbage Collector die Objekte hin und her. Auf ein Objekt zu zeigen ist natürlich wertlos, wenn sich seine Adresse unvorhersehbar ändern könnte. Die Lösung dieser Problematik bietet die fixed-Anweisung. fixed weist den Garbage Collector an, das Objekt zu »fixieren« – es wird danach nicht mehr verlagert. Da sich dies auf das Verhalten der Laufzeitumgebung auswirken kann, sollten als fixed deklarierte Blöcke nur kurzzeitig benutzt werden.

Hinter der fixed-Anweisung wird in runden Klammern ein Zeiger auf eine verwaltete Variable festgelegt. Diese Variable ist diejenige, die während der Ausführung fixiert wird.


fixed (Typ* pointer = Ausdruck) {
   ...
}

Ausdruck muss implizit in Typ* konvertierbar sein.

Am besten sind die Wirkungsweise und der Einsatz von fixed anhand eines Beispiels zu verstehen. Sehen Sie sich daher zuerst das folgende Codefragment an:


class Program {
  int intVar; 
  static void Main() {
    Program obj = new Program();
    // unsicherer Code
    unsafe {
      // fixierter Code
      fixed(int* pointer = &obj.intVar) { 
        *pointer = 9;
        System.Console.WriteLine(*pointer);  
      }  
    }  
  }
}

Im Code wird ein Objekt vom Typ Program in Main erzeugt. Es kann grundsätzlich nicht garantiert werden, dass das Program-Objekt obj vom Garbage Collector nicht im Speicher verschoben wird. Da der Zeiger pointer auf das objekteigene Feld intVar verweist, muss sichergestellt sein, dass sich das Objekt bei der Auswertung des Zeigers immer noch an derselben physikalischen Adresse befindet. Die fixed-Anweisung mit der Angabe, worauf pointer zeigt, garantiert, dass die Dereferenzierung an der Konsole das richtige Ergebnis ausgibt.

Beachten Sie, dass in diesem Beispiel nicht die gesamte Methode als unsicher markiert ist, sondern nur der Kontext, in dem der Zeiger eine Rolle spielt.


Galileo Computing

7.7.4 Zeigerarithmetik  downtop

Sie können in C# Zeiger addieren und subtrahieren so wie in C oder in anderen Sprachen. Dazu bedient sich der C#-Compiler intern des sizeof-Operators, der die Anzahl der Bytes zurückgibt, die von einer Variablen des angegebenen Typs belegt werden. Addieren Sie beispielsweise zu einem Zeiger vom Typ int* den Wert 1, verweist der Zeiger auf eine Adresse, die um 4 Byte höher liegt, da ein Integer eine Breite von 4 Byte hat.

Im folgenden Beispiel wird ein int-Array initialisiert. Anschließend werden die Inhalte der Array-Elemente nicht wie üblich über ihren Index, sondern mittels Zeigerarithmetik an der Konsole ausgegeben.


class Program {
  unsafe static void Main(string[] args) {
    int[] arr = {10, 72, 333, 4550};
    fixed(int* pointer = arr) {
      Console.WriteLine(*pointer);
      Console.WriteLine(*(pointer + 1));
      Console.WriteLine(*(pointer + 2));
      Console.WriteLine(*(pointer + 3));
    }
  }
}

Ein Array ist den Referenztypen und damit den verwalteten Typen zuzurechnen. Der C#-Compiler erlaubt es aber nicht, außerhalb einer fixed-Anweisung mit einem Zeiger auf einen verwalteten Typ zu zeigen. Mit


fixed(int* pointer = arr)

kommen wir dieser Forderung nach. Das Array arr wird implizit in den Typ int* konvertiert und ist gleichwertig mit der Anweisung


int* pointer = &arr[0]

In der ersten Ausgabeanweisung wird pointer dereferenziert und der Inhalt 10 angezeigt, weil ein Zeiger auf ein Array immer auf das erste Element zeigt. In den folgenden Ausgaben wird die Ausgabeadresse des Zeigers um jeweils eine Integerkapazität erhöht, also um jeweils 4 Byte. Da die Elemente eines Arrays direkt hintereinander im Speicher abgelegt sind, werden der Reihe nach die Zahlen 72, 333 und 4550 an der Konsole angezeigt.


Galileo Computing

7.7.5 Der Operator »->«  toptop

Strukturen sind Wertetypen aus mehreren verschiedenen Elementen auf dem Stack und können ebenfalls über Zeiger angesprochen werden. Nehmen wir an, es sei die Struktur Point wie folgt definiert:


public struct Point {
  public int X;
  public int Y;
}

Innerhalb eines unsicheren Kontexts können wir uns mit


Point point = new Point();
Point* ptr = &point;

einen Zeiger auf ein Objekt vom Typ Point besorgen. Beabsichtigen wir, das Feld X zu manipulieren und ihm den Wert 150 zuzuweisen, muss der Zeiger ptr zuerst dereferenziert werden. Auf das Ergebnis kann mittels Punktnotation auf das Member zugegriffen werden, dem die Zahl zugewiesen werden soll. Der gesamte Ausdruck sieht dann wie folgt aus:


(*ptr).X = 150;

C# bietet uns mit dem Operator »->« eine einfache Kombination aus Dereferenzierung und Feldzugriff an. Der Ausdruck kann daher gleichwertig auch so formuliert werden:


ptr->X = 150;

 << zurück
  
  Zum Katalog
Zum Katalog: Visual C# 2005
Visual C# 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Visual Basic 2005






 Visual Basic 2005


Zum Katalog: Java ist auch eine Insel






 Java ist auch eine
 Insel


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de